/*
 * Copyright 2023, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */

#include <KernelExport.h>
#include <PCI.h>
#include <OS.h>
#include <graphic_driver.h>
#include <malloc.h>
#include <string.h>
#include <stdio.h>

#include "device.h"

#define TRACE_DEVICE
#ifdef TRACE_DEVICE
#define TRACE(x...) dprintf(DRIVER_PREFIX ": " x)
#else
#define TRACE(x...) ;
#endif

#define ERROR(x...) dprintf(DRIVER_PREFIX ": " x)

// 外部变量
extern pci_module_info *gPCI;
device_hooks gDeviceHooks = {
    device_open,
    device_close,
    device_free,
    device_ioctl,
    device_read,
    device_write,
    NULL,
    NULL,
    NULL,
    NULL};

// 映射设备寄存器和帧缓冲
static status_t
map_device_memory(device_info *di)
{
    char buffer[B_OS_NAME_LENGTH];
    ls7a2000_shared_info *si = di->si;
    pci_info *pcii = &di->pcii;

    // 使能PCI内存访问和总线主控
    uint16 command = gPCI->read_pci_config(pcii->bus, pcii->device, pcii->function, PCI_command, 2);
    command |= PCI_command_memory | PCI_command_master;
    gPCI->write_pci_config(pcii->bus, pcii->device, pcii->function, PCI_command, 2, command);

    // 映射寄存器空间
    sprintf(buffer, DEVICE_FORMAT " regs",
            pcii->vendor_id, pcii->device_id,
            pcii->bus, pcii->device, pcii->function);

    // 寄存器通常在BAR0
    si->regs_area = map_physical_memory(
        buffer,
        pcii->u.h0.base_registers_pci[0],
        pcii->u.h0.base_register_sizes[0],
        B_ANY_KERNEL_ADDRESS,
        B_READ_AREA | B_WRITE_AREA,
        (void **)&di->regs);

    if (si->regs_area < 0)
    {
        ERROR("Failed to map registers\n");
        return si->regs_area;
    }

    // 映射帧缓冲空间
    sprintf(buffer, DEVICE_FORMAT " framebuffer",
            pcii->vendor_id, pcii->device_id,
            pcii->bus, pcii->device, pcii->function);

    // 帧缓冲通常在BAR1
    si->frame_buffer_area = map_physical_memory(
        buffer,
        pcii->u.h0.base_registers_pci[1],
        pcii->u.h0.base_register_sizes[1],
        B_ANY_KERNEL_BLOCK_ADDRESS | B_WRITE_COMBINING_MEMORY,
        B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA,
        (void **)&si->frame_buffer);

    // 如果写组合内存映射失败，尝试普通映射
    if (si->frame_buffer_area < 0)
    {
        si->frame_buffer_area = map_physical_memory(
            buffer,
            pcii->u.h0.base_registers_pci[1],
            pcii->u.h0.base_register_sizes[1],
            B_ANY_KERNEL_BLOCK_ADDRESS,
            B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA,
            (void **)&si->frame_buffer);
    }

    if (si->frame_buffer_area < 0)
    {
        ERROR("Failed to map framebuffer\n");
        delete_area(si->regs_area);
        si->regs_area = -1;
        return si->frame_buffer_area;
    }

    // 保存帧缓冲物理地址和大小
    si->physical_frame_buffer = pcii->u.h0.base_registers_pci[1];
    si->frame_buffer_size = pcii->u.h0.base_register_sizes[1];

    // 映射光标缓冲区
    sprintf(buffer, DEVICE_FORMAT " cursor",
            pcii->vendor_id, pcii->device_id,
            pcii->bus, pcii->device, pcii->function);

    // 分配光标缓冲区（通常为64x64像素，ARGB格式）
    si->cursor_buffer_size = 64 * 64 * 4;
    si->cursor_area = create_area(
        buffer,
        (void **)&si->cursor_buffer,
        B_ANY_KERNEL_ADDRESS,
        ROUND_TO_PAGE_SIZE(si->cursor_buffer_size),
        B_FULL_LOCK,
        B_READ_AREA | B_WRITE_AREA);

    if (si->cursor_area < 0)
    {
        ERROR("Failed to create cursor buffer\n");
        delete_area(si->frame_buffer_area);
        delete_area(si->regs_area);
        si->frame_buffer_area = -1;
        si->regs_area = -1;
        return si->cursor_area;
    }

    // 获取光标缓冲区物理地址
    physical_entry pe;
    if (get_memory_map(si->cursor_buffer, si->cursor_buffer_size, &pe, 1) != B_OK)
    {
        ERROR("Failed to get cursor buffer physical address\n");
        delete_area(si->cursor_area);
        delete_area(si->frame_buffer_area);
        delete_area(si->regs_area);
        si->cursor_area = -1;
        si->frame_buffer_area = -1;
        si->regs_area = -1;
        return B_ERROR;
    }

    si->physical_cursor_buffer = pe.address;

    // 初始化硬件参数
    si->cursor_format = B_RGBA32;
    si->cursor_width = 64;
    si->cursor_height = 64;
    si->cursor_hot_x = 0;
    si->cursor_hot_y = 0;
    si->cursor_visible = false;
    si->cursor_x = 0;
    si->cursor_y = 0;

    // 读取硬件版本
    si->revision = RREG32(LS7A_DC_REVISION);

    // 检查是否支持硬件光标
    si->hardware_cursor = true;

    TRACE("Device mapped successfully: regs=%p, fb=%p, cursor=%p\n",
          di->regs, si->frame_buffer, si->cursor_buffer);
    TRACE("Framebuffer: phys=0x%lx, size=%lu\n",
          si->physical_frame_buffer, si->frame_buffer_size);
    TRACE("Hardware revision: 0x%lx\n", si->revision);

    return B_OK;
}

// 取消映射设备内存
static void
unmap_device_memory(device_info *di)
{
    ls7a2000_shared_info *si = di->si;

    if (si->cursor_area >= 0)
        delete_area(si->cursor_area);

    if (si->frame_buffer_area >= 0)
        delete_area(si->frame_buffer_area);

    if (si->regs_area >= 0)
        delete_area(si->regs_area);

    si->cursor_area = -1;
    si->frame_buffer_area = -1;
    si->regs_area = -1;
}

// 初始化硬件光标
static status_t
init_hardware_cursor(device_info *di)
{
    ls7a2000_shared_info *si = di->si;

    // 设置光标地址
    WREG32(LS7A_DC_CURSOR_ADDR, (uint32)si->physical_cursor_buffer);

    // 设置光标颜色（ARGB格式）
    WREG32(LS7A_DC_CURSOR_FG, 0xFFFFFFFF); // 白色
    WREG32(LS7A_DC_CURSOR_BG, 0x00000000); // 黑色

    // 初始化光标位置
    WREG32(LS7A_DC_CURSOR_POS, (si->cursor_y << 16) | si->cursor_x);

    // 清空光标缓冲区
    memset(si->cursor_buffer, 0, si->cursor_buffer_size);

    return B_OK;
}

// 读取EDID数据
static status_t
read_edid(device_info *di)
{
    ls7a2000_shared_info *si = di->si;
    bool has_edid = false;
    uint8 checksum = 0;

    // 读取EDID数据（128字节）
    for (int i = 0; i < 128; i++)
    {
        si->edid[i] = RREG32(LS7A_DC_EDID_START + i * 4) & 0xFF;
        checksum += si->edid[i];
    }

    // 验证EDID头部标识（前8字节应该是固定的0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00）
    if (si->edid[0] == 0x00 && si->edid[1] == 0xFF && si->edid[2] == 0xFF && si->edid[3] == 0xFF &&
        si->edid[4] == 0xFF && si->edid[5] == 0xFF && si->edid[6] == 0xFF && si->edid[7] == 0x00)
    {
        // 验证校验和（所有字节加起来应该是0）
        if (checksum == 0)
        {
            has_edid = true;
            TRACE("Valid EDID data found\n");

            // 打印显示器信息
            char manufacturer[4];
            manufacturer[0] = 'A' + ((si->edid[8] & 0x7C) >> 2) - 1;
            manufacturer[1] = 'A' + (((si->edid[8] & 0x03) << 3) | ((si->edid[9] & 0xE0) >> 5)) - 1;
            manufacturer[2] = 'A' + (si->edid[9] & 0x1F) - 1;
            manufacturer[3] = 0;

            uint16 product_id = si->edid[10] | (si->edid[11] << 8);
            uint32 serial = si->edid[12] | (si->edid[13] << 8) | (si->edid[14] << 16) | (si->edid[15] << 24);

            TRACE("Monitor: %s, Product ID: %u, Serial: %lu\n", manufacturer, product_id, serial);
        }
        else
        {
            ERROR("EDID checksum invalid\n");
        }
    }
    else
    {
        ERROR("EDID header invalid\n");
    }

    si->has_edid = has_edid;
    return has_edid ? B_OK : B_ERROR;
}

// 初始化显示控制器
static status_t
init_display_controller(device_info *di)
{
    ls7a2000_shared_info *si = di->si;

    // 复位显示控制器
    WREG32(LS7A_DC_RESET, 1);
    snooze(10000); // 等待10ms
    WREG32(LS7A_DC_RESET, 0);

    // 设置默认显示模式（1024x768@60Hz，32位色深）
    si->width = 1024;
    si->height = 768;
    si->bits_per_pixel = 32;
    si->bytes_per_row = si->width * (si->bits_per_pixel / 8);
    si->color_space = B_RGB32;
    si->current_mode = DISPLAY_MODE_1024_768_60HZ;

    // 设置帧缓冲地址
    WREG32(LS7A_DC_FB_ADDR, (uint32)si->physical_frame_buffer);

    // 设置帧缓冲行跨度
    WREG32(LS7A_DC_FB_STRIDE, si->bytes_per_row);

    // 设置帧缓冲原点
    WREG32(LS7A_DC_FB_ORIGIN, 0);

    // 设置水平显示参数
    // 1024x768@60Hz: 总像素=1344, 同步=136, 前肩=24, 后肩=160
    WREG32(LS7A_DC_HDISPLAY, (1024 - 1) | ((1344 - 1) << 16));
    WREG32(LS7A_DC_HSYNC, (1024 + 24) | ((1024 + 24 + 136) << 16));

    // 设置垂直显示参数
    // 1024x768@60Hz: 总行数=806, 同步=6, 前肩=3, 后肩=29
    WREG32(LS7A_DC_VDISPLAY, (768 - 1) | ((806 - 1) << 16));
    WREG32(LS7A_DC_VSYNC, (768 + 3) | ((768 + 3 + 6) << 16));

    // 设置配置寄存器
    uint32 config = LS7A_DC_CONFIG_FORMAT_32BPP | // 32位每像素
                    LS7A_DC_CONFIG_HSYNC_POL |    // 水平同步极性
                    LS7A_DC_CONFIG_VSYNC_POL;     // 垂直同步极性

    // 初始化硬件光标
    init_hardware_cursor(di);

    // 读取EDID数据
    read_edid(di);

    // 使能显示控制器和硬件光标
    config |= LS7A_DC_CONFIG_ENABLE | LS7A_DC_CONFIG_CURSOR;
    WREG32(LS7A_DC_CONFIG, config);

    // 设置DPMS模式为开启
    si->dpms_mode = B_DPMS_ON;

    return B_OK;
}

// 初始化设备
status_t
device_init(device_info *di)
{
    // 创建共享信息区域
    char buffer[B_OS_NAME_LENGTH];
    sprintf(buffer, DEVICE_FORMAT " shared",
            di->pcii.vendor_id, di->pcii.device_id,
            di->pcii.bus, di->pcii.device, di->pcii.function);

    // 创建共享区域
    di->shared_area = create_area(buffer,
                                  (void **)&di->si, B_ANY_KERNEL_ADDRESS,
                                  ROUND_TO_PAGE_SIZE(sizeof(ls7a2000_shared_info)),
                                  B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);

    if (di->shared_area < 0)
    {
        ERROR("Failed to create shared area\n");
        return di->shared_area;
    }

    // 初始化共享信息
    memset(di->si, 0, sizeof(ls7a2000_shared_info));
    di->si->area = di->shared_area;
    di->si->magic = LS7A2000_PRIVATE_DATA_MAGIC;

    // 映射设备内存
    status_t status = map_device_memory(di);
    if (status != B_OK)
    {
        ERROR("Failed to map device memory\n");
        delete_area(di->shared_area);
        di->shared_area = -1;
        di->si = NULL;
        return status;
    }

    // 初始化显示控制器
    status = init_display_controller(di);
    if (status != B_OK)
    {
        ERROR("Failed to initialize display controller\n");
        unmap_device_memory(di);
        delete_area(di->shared_area);
        di->shared_area = -1;
        di->si = NULL;
        return status;
    }

    TRACE("Device initialized successfully\n");
    return B_OK;
}

// 卸载设备
void device_uninit(device_info *di)
{
    TRACE("device_uninit()\n");

    if (di->si != NULL)
    {
        // 关闭显示控制器
        WREG32(LS7A_DC_CONFIG, 0);

        // 取消映射设备内存
        unmap_device_memory(di);

        // 删除共享区域
        delete_area(di->shared_area);
        di->shared_area = -1;
        di->si = NULL;
    }
}

// 打开设备
status_t
device_open(device_info *di, uint32 flags)
{
    TRACE("device_open()\n");

    // 检查设备是否已经打开
    if (di->is_open)
        return B_BUSY;

    // 标记设备为打开状态
    di->is_open = true;

    return B_OK;
}

// 关闭设备
status_t
device_close(device_info *di)
{
    TRACE("device_close()\n");

    // 标记设备为关闭状态
    di->is_open = false;

    return B_OK;
}

// 释放设备
status_t
device_free(device_info *di)
{
    TRACE("device_free()\n");

    // 确保设备已关闭
    if (di->is_open)
        return B_BUSY;

    return B_OK;
}

// 读取设备
status_t
device_read(device_info *di, off_t pos, void *buf, size_t *len)
{
    TRACE("device_read()\n");

    *len = 0;
    return B_NOT_ALLOWED;
}

// 写入设备
status_t
device_write(device_info *di, off_t pos, const void *buf, size_t *len)
{
    TRACE("device_write()\n");

    *len = 0;
    return B_NOT_ALLOWED;
}

// 设置显示模式
static status_t
set_display_mode(device_info *di, ls7a2000_display_mode *mode)
{
    ls7a2000_shared_info *si = di->si;
    uint32 config;

    TRACE("set_display_mode(): %dx%d@%d, %d bpp\n",
          mode->width, mode->height, mode->refresh_rate, mode->bits_per_pixel);

    // 保存当前配置
    config = RREG32(LS7A_DC_CONFIG);

    // 禁用显示控制器
    WREG32(LS7A_DC_CONFIG, config & ~LS7A_DC_CONFIG_ENABLE);

    // 更新显示参数
    si->width = mode->width;
    si->height = mode->height;
    si->bits_per_pixel = mode->bits_per_pixel;
    si->bytes_per_row = si->width * (si->bits_per_pixel / 8);
    si->color_space = mode->color_space;
    si->current_mode = mode->flags & DISPLAY_MODE_MASK;

    // 设置帧缓冲行跨度
    WREG32(LS7A_DC_FB_STRIDE, si->bytes_per_row);

    // 设置颜色深度
    config &= ~LS7A_DC_CONFIG_FORMAT_MASK;
    switch (si->bits_per_pixel)
    {
    case 8:
        config |= LS7A_DC_CONFIG_FORMAT_8BPP;
        break;
    case 16:
        config |= LS7A_DC_CONFIG_FORMAT_16BPP;
        break;
    case 24:
        config |= LS7A_DC_CONFIG_FORMAT_24BPP;
        break;
    case 32:
    default:
        config |= LS7A_DC_CONFIG_FORMAT_32BPP;
        break;
    }

    // 设置显示参数
    switch (si->current_mode)
    {
    case DISPLAY_MODE_640_480_60HZ:
        // 640x480@60Hz: 总像素=800, 同步=96, 前肩=16, 后肩=48
        WREG32(LS7A_DC_HDISPLAY, (640 - 1) | ((800 - 1) << 16));
        WREG32(LS7A_DC_HSYNC, (640 + 16) | ((640 + 16 + 96) << 16));
        // 总行数=525, 同步=2, 前肩=10, 后肩=33
        WREG32(LS7A_DC_VDISPLAY, (480 - 1) | ((525 - 1) << 16));
        WREG32(LS7A_DC_VSYNC, (480 + 10) | ((480 + 10 + 2) << 16));
        break;
    case DISPLAY_MODE_800_600_60HZ:
        // 800x600@60Hz: 总像素=1056, 同步=128, 前肩=40, 后肩=88
        WREG32(LS7A_DC_HDISPLAY, (800 - 1) | ((1056 - 1) << 16));
        WREG32(LS7A_DC_HSYNC, (800 + 40) | ((800 + 40 + 128) << 16));
        // 总行数=628, 同步=4, 前肩=1, 后肩=23
        WREG32(LS7A_DC_VDISPLAY, (600 - 1) | ((628 - 1) << 16));
        WREG32(LS7A_DC_VSYNC, (600 + 1) | ((600 + 1 + 4) << 16));
        break;
    case DISPLAY_MODE_1024_768_60HZ:
    default:
        // 1024x768@60Hz: 总像素=1344, 同步=136, 前肩=24, 后肩=160
        WREG32(LS7A_DC_HDISPLAY, (1024 - 1) | ((1344 - 1) << 16));
        WREG32(LS7A_DC_HSYNC, (1024 + 24) | ((1024 + 24 + 136) << 16));
        // 总行数=806, 同步=6, 前肩=3, 后肩=29
        WREG32(LS7A_DC_VDISPLAY, (768 - 1) | ((806 - 1) << 16));
        WREG32(LS7A_DC_VSYNC, (768 + 3) | ((768 + 3 + 6) << 16));
        break;
    case DISPLAY_MODE_1280_1024_60HZ:
        // 1280x1024@60Hz: 总像素=1688, 同步=112, 前肩=48, 后肩=248
        WREG32(LS7A_DC_HDISPLAY, (1280 - 1) | ((1688 - 1) << 16));
        WREG32(LS7A_DC_HSYNC, (1280 + 48) | ((1280 + 48 + 112) << 16));
        // 总行数=1066, 同步=3, 前肩=1, 后肩=38
        WREG32(LS7A_DC_VDISPLAY, (1024 - 1) | ((1066 - 1) << 16));
        WREG32(LS7A_DC_VSYNC, (1024 + 1) | ((1024 + 1 + 3) << 16));
        break;
    case DISPLAY_MODE_1440_900_60HZ:
        // 1440x900@60Hz: 总像素=1904, 同步=152, 前肩=80, 后肩=232
        WREG32(LS7A_DC_HDISPLAY, (1440 - 1) | ((1904 - 1) << 16));
        WREG32(LS7A_DC_HSYNC, (1440 + 80) | ((1440 + 80 + 152) << 16));
        // 总行数=932, 同步=3, 前肩=1, 后肩=28
        WREG32(LS7A_DC_VDISPLAY, (900 - 1) | ((932 - 1) << 16));
        WREG32(LS7A_DC_VSYNC, (900 + 1) | ((900 + 1 + 3) << 16));
        break;
    case DISPLAY_MODE_1680_1050_60HZ:
        // 1680x1050@60Hz: 总像素=2240, 同步=176, 前肩=104, 后肩=280
        WREG32(LS7A_DC_HDISPLAY, (1680 - 1) | ((2240 - 1) << 16));
        WREG32(LS7A_DC_HSYNC, (1680 + 104) | ((1680 + 104 + 176) << 16));
        // 总行数=1089, 同步=3, 前肩=1, 后肩=35
        WREG32(LS7A_DC_VDISPLAY, (1050 - 1) | ((1089 - 1) << 16));
        WREG32(LS7A_DC_VSYNC, (1050 + 1) | ((1050 + 1 + 3) << 16));
        break;
    case DISPLAY_MODE_1920_1080_60HZ:
        // 1920x1080@60Hz: 总像素=2200, 同步=44, 前肩=88, 后肩=148
        WREG32(LS7A_DC_HDISPLAY, (1920 - 1) | ((2200 - 1) << 16));
        WREG32(LS7A_DC_HSYNC, (1920 + 88) | ((1920 + 88 + 44) << 16));
        // 总行数=1125, 同步=5, 前肩=4, 后肩=36
        WREG32(LS7A_DC_VDISPLAY, (1080 - 1) | ((1125 - 1) << 16));
        WREG32(LS7A_DC_VSYNC, (1080 + 4) | ((1080 + 4 + 5) << 16));
        break;
    case DISPLAY_MODE_1920_1200_60HZ:
        // 1920x1200@60Hz: 总像素=2592, 同步=200, 前肩=136, 后肩=336
        WREG32(LS7A_DC_HDISPLAY, (1920 - 1) | ((2592 - 1) << 16));
        WREG32(LS7A_DC_HSYNC, (1920 + 136) | ((1920 + 136 + 200) << 16));
        // 总行数=1242, 同步=6, 前肩=3, 后肩=33
        WREG32(LS7A_DC_VDISPLAY, (1200 - 1) | ((1242 - 1) << 16));
        WREG32(LS7A_DC_VSYNC, (1200 + 3) | ((1200 + 3 + 6) << 16));
        break;
    }

    // 重新启用显示控制器
    WREG32(LS7A_DC_CONFIG, config | LS7A_DC_CONFIG_ENABLE);

    return B_OK;
}

// 设置DPMS模式
static status_t
set_dpms_mode(device_info *di, uint32 dpms_mode)
{
    ls7a2000_shared_info *si = di->si;
    uint32 config;

    TRACE("set_dpms_mode(): %lu\n", dpms_mode);

    // 保存当前配置
    config = RREG32(LS7A_DC_CONFIG);

    switch (dpms_mode)
    {
    case B_DPMS_ON:
        // 启用显示控制器
        config |= LS7A_DC_CONFIG_ENABLE;
        break;
    case B_DPMS_STAND_BY:
    case B_DPMS_SUSPEND:
        // 禁用显示控制器但保持其他设置
        config &= ~LS7A_DC_CONFIG_ENABLE;
        break;
    case B_DPMS_OFF:
        // 完全禁用显示控制器
        config &= ~(LS7A_DC_CONFIG_ENABLE | LS7A_DC_CONFIG_CURSOR);
        break;
    default:
        return B_ERROR;
    }

    // 更新配置寄存器
    WREG32(LS7A_DC_CONFIG, config);

    // 保存DPMS模式
    si->dpms_mode = dpms_mode;

    return B_OK;
}